package com.xing.asm.simple2;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.AdviceAdapter;
import org.objectweb.asm.commons.Method;

public class MethodWriter extends org.objectweb.asm.ClassVisitor implements Opcodes {

    public MethodWriter(final org.objectweb.asm.ClassVisitor cv) {
        super(Opcodes.ASM5, cv);
    }

    protected final Type TYPE_API = Type.getType(Api.class);
    protected final Type TYPE_ADVICEAPI = Type.getType(AdiceApi.class);
    protected final Type TYPE_THROWABLE = Type.getType(Throwable.class);
    //            protected final Type TYPE_OBJECT = Type.getType(Object.class);
//            protected final Type TYPE_OBJECT_ARRAY = Type.getType(Object[].class);
    protected final Method METHOD_METHOD_BEFORE = Method.getMethod("void doBefore(Object[])");
    protected final Method METHOD_METHOD_AFTER = Method.getMethod("void doAfter()");
    private int adviceApiIndex = -1;
    private Label exitStart;
    private Label exitEnd;
    private Label exitHandler;

    @Override
    public AnnotationVisitor visitAnnotation(String s, boolean b) {
        return super.visitAnnotation(s, b);
    }


    public MethodVisitor visitMethod(int access, String name, final String desc, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
        if(name.contains("excute")) {
            mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {
                @Override
                protected void onMethodEnter() {
                    push((Type)null);

                    adviceApiIndex = newLocal(TYPE_ADVICEAPI);
                    storeLocal(adviceApiIndex);

                    Label startCatch = newLabel();
                    Label endCatch = newLabel();

                    mark(startCatch);

                    invokeStatic(TYPE_API, new Method("createAdviceApi", TYPE_ADVICEAPI, new Type[0]));
                    storeLocal(adviceApiIndex);
                    loadLocal(adviceApiIndex);
                    //Type[] argumentTypes = Type.getArgumentTypes(desc);
                    //new Object[]{} 加载方法参数
                    loadArgArray();
                    invokeVirtual(TYPE_ADVICEAPI, METHOD_METHOD_BEFORE);

                    goTo(endCatch);

                    Label handler = new Label();
                    visitLabel(handler);

                    final int throwableLocal = newLocal(TYPE_THROWABLE);
                    storeLocal(throwableLocal);

                    mark(endCatch);
                    visitTryCatchBlock(startCatch, endCatch, handler, TYPE_THROWABLE.getInternalName());

                }

                @Override
                protected void onMethodExit(int opcode) {
                    if(adviceApiIndex != -1) {
                        exitStart = newLabel();
                        exitEnd = newLabel();

                        mark(exitStart);

                        loadLocal(adviceApiIndex);
                        invokeVirtual(TYPE_ADVICEAPI, METHOD_METHOD_AFTER);

                        goTo(exitEnd);

                        exitHandler = new Label();
                        mark(exitHandler);

                        final int throwableLocal = newLocal(TYPE_THROWABLE);
                        storeLocal(throwableLocal);

                        mark(exitEnd);

                        visitTryCatchBlock(exitStart, exitEnd, exitHandler, TYPE_THROWABLE.getInternalName());
                    }
                }

                @Override
                public void visitMaxs(int maxStack, int maxLocals) {
//                            visitTryCatchBlock(exitStart, exitEnd, exitHandler, TYPE_THROWABLE.getInternalName());
//                            System.err.println("fuck you ");
                    super.visitMaxs(maxStack, maxLocals);
                }
            };
        }
        return mv;

    }
}
